module TypesToC
  ( tyToC,
    tyToCLambdaFix,
    tyToCRawFunctionPtrFix,
  )
where

import Data.List
import Data.Text (pack, splitOn, unpack)
import SymPath
import Types
import Util

tyToC :: Ty -> String
tyToC = tyToCManglePtr False

tyToCLambdaFix :: Ty -> String
tyToCLambdaFix FuncTy {} = "Lambda"
tyToCLambdaFix (RefTy FuncTy {} _) = "Lambda*"
tyToCLambdaFix (RefTy (RefTy FuncTy {} _) _) = "Lambda**"
tyToCLambdaFix (RefTy (RefTy (RefTy FuncTy {} _) _) _) = "Lambda***" -- TODO: More cases needed?! What's a better way to do it..?
tyToCLambdaFix t = tyToCManglePtr False t

tyToCRawFunctionPtrFix :: Ty -> String
tyToCRawFunctionPtrFix FuncTy {} = "void*"
tyToCRawFunctionPtrFix t = tyToCManglePtr False t

tyToCManglePtr :: Bool -> Ty -> String
tyToCManglePtr b (StructTy (ConcreteNameTy (SymPath [] "Box")) [t]) = tyToCManglePtr b t ++ (if b then mangle "*" else "*")
tyToCManglePtr b (PointerTy p) = tyToCManglePtr b p ++ (if b then mangle "*" else "*")
tyToCManglePtr b (RefTy r _) = tyToCManglePtr b r ++ (if b then mangle "*" else "*")
tyToCManglePtr _ ty = f ty
  where
    f IntTy = "int"
    f BoolTy = "bool"
    f FloatTy = "float"
    f DoubleTy = "double"
    f LongTy = "Long"
    f ByteTy = "uint8_t"
    f StringTy = "String"
    f PatternTy = "Pattern"
    f CharTy = "Char"
    f CCharTy = "CChar"
    f UnitTy = "void"
    f (VarTy x) = x
    f (FuncTy argTys retTy _) = "Fn__" ++ joinWithUnderscore (map (tyToCManglePtr True) argTys) ++ "_" ++ tyToCManglePtr True retTy
    f (StructTy s []) = tyToCManglePtr False s
    f (StructTy s typeArgs) = tyToCManglePtr False s ++ "__" ++ joinWithUnderscore (map (tyToCManglePtr True) typeArgs)
    f (ConcreteNameTy spath) = mangle (intercalate "" (map unpack (splitOn (pack ".") (pack (show spath)))))
    f ModuleTy = err "modules"
    f TypeTy = err "types"
    f MacroTy = err "macros"
    f DynamicTy = err "dynamic functions"
    f StaticLifetimeTy = err "lifetimes"
    f InterfaceTy = err "interfaces"
    f Universe = err "universe"
    f (PointerTy _) = err "pointers"
    f (RefTy _ _) = err "references"
    f CTy = "c_code" -- Literal C; we shouldn't emit anything.
    err s = error ("Can't emit the type of " ++ s ++ ".")
